package org.byqh.dqeai.controller;

import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.byqh.dqeai.common.Json;
import org.byqh.dqeai.dto.common.IPage;
import org.byqh.dqeai.dto.common.SysPageDTO;
import org.byqh.dqeai.entity.SysAppFileCategory;
import org.byqh.dqeai.entity.SysAppModuleFileCategory;
import org.byqh.dqeai.entity.SysFile;
import org.byqh.dqeai.entity.proxy.SysFileProxy;
import org.byqh.dqeai.service.UploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

@RestController
@RequestMapping("/sysFile")
@Tag(name = "系统[SysFile] - 文件管理")
public class SysFileController
        extends BaseController<SysFile, SysFileProxy> {

    @Autowired
    private UploadService service;

    /**
     * @param page
     * @param size
     * @param dto
     */
    @PostMapping(value = "/list")
    public Json<IPage<SysFile>> list(@RequestParam(defaultValue = "1") Integer page,
                                     @RequestParam(defaultValue = "10") Integer size,
                                     @RequestParam(defaultValue = "", required = false) String sort,
                                     @RequestBody(required = false) SysFile dto) {

        return listPageImpl(SysFile.class, page, size, (query) -> {
            if (dto != null) {
                if (!StringUtils.isBlank(dto.getCategoryId())) {
                    query = query.where(r -> r.categoryId().eq(dto.getCategoryId()));
                }

                if (!StringUtils.isBlank(dto.getParentFileId())) {
                    query = query.where(r -> r.parentFileId().eq(dto.getParentFileId()));
                } else {
                    query = query.where(r -> r.parentFileId().isNull());
                }
            } else {
                query = query.whereById("-1");
            }

            return query;
        });
    }

    @PostMapping(value = "/listAll")
    public Json<IPage<SysFile>> listAll(@RequestParam(defaultValue = "1") Integer page,
                                        @RequestParam(defaultValue = "10") Integer size,
                                        @RequestParam(defaultValue = "", required = false) String sort,
                                        @RequestBody(required = false) SysFile dto) {

        var query = this.getEntityQueryable();

        if (dto != null) {
            query = query.where(r -> r.categoryId().eq(dto.getCategoryId()));
        }

        var pageDto = new SysPageDTO();
        pageDto.setPage(page);
        pageDto.setSize(size);

        return getPageResult(query, pageDto);
    }

    /**
     * 文件分片上传
     */
    @PostMapping(value = "/uploadFileChunk",
            headers = {"content-type=multipart/mixed", "content-type=multipart/form-data"})
    public Json uploadFileChunk(@RequestPart(name = "file") MultipartFile file,
                                @RequestParam() String fileHash,
                                @RequestParam() Integer chunk,
                                @RequestParam() String hash
    ) {
        try {
            this.service.upload(file, fileHash, chunk, hash);

            return Json.success();
        } catch (Exception ex) {
            return Json.exception(ex);
        }
    }

    /**
     * 文件分片上传验证
     */
    @PostMapping(value = "/checkUploadFileChunk")
    public Json checkUploadFileChunk(@RequestParam() String fileHash,
                                     @RequestParam() Integer chunk,
                                     @RequestParam() String hash) throws IOException {
        if (this.service.checkFileChunk(fileHash, chunk, hash)) {
            return Json.success();
        }

        return Json.error();
    }

    /**
     * 合并文件上传
     */
    @PostMapping(value = "/combineUploadFile")
    public Json combineUploadFile(@RequestParam() String fileHash, @RequestParam() String filename) throws IOException {
        if (this.service.combine(fileHash, filename)) {
            return Json.success();
        }

        return Json.error();
    }

    /**
     * 文件上传验证
     */
    @PostMapping(value = "/checkUploadFile")
    public Json checkUploadFile(@RequestParam() String fileHash) throws IOException {
        if (this.service.checkFile(fileHash)) {
            return Json.success();
        }

        return Json.error();
    }

    /**
     * 文件下载
     */
    @GetMapping(value = "/download", produces = "application/octet-stream")
    public void download(@RequestParam() String fileHash,
                         @RequestParam() Integer chunk,
                         @RequestParam() Integer chunkSize,
                         HttpServletResponse response) {
        try {
            byte[] bytes = this.service.download(fileHash, chunk, chunkSize);

            response.addHeader("ChunkHash", this.service.getHash(bytes));
            response.getOutputStream().write(bytes);
            response.flushBuffer();
        } catch (IOException ex) {

            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "文件不存在!");
        }
    }

    /**
     * 通过appId系统文件管理数据
     */
    @GetMapping(value = "/findByCategoryId/{appId}")
    public Json<List<SysFile>> findByCategoryId(@PathVariable String appId) {
        try {
            return Json.success(this.findByCategoryIdCore(appId));
        } catch (IOException e) {
            return Json.exceptionT(e);
        }
    }

    @Override
    @DeleteMapping(value = "/{id}")
    public Json<Boolean> delete(String id) {
        try {
            return Json.success(this.deleteCascade(id));
        } catch (IOException e) {
            return Json.exceptionT(e);
        }
    }

    /**
     * 通过appModuleId系统文件管理数据
     */
    @GetMapping(value = "/findByAppModuleId/{appModuleId}")
    public Json<List<SysFile>> findByAppModuleId(@PathVariable String appModuleId) {
        try {
            return Json.success(this.findByAppModuleIdCore(appModuleId));
        } catch (IOException e) {
            return Json.exceptionT(e);
        }
    }

    public boolean deleteCascade(String id) throws IOException {
        String sql = String.format("""
                SELECT DISTINCT *
                        FROM SYS_FILE
                                 START WITH FILE_ID = '%s'
                        CONNECT BY PRIOR FILE_ID = PARENT_FILE_ID
                """, id);

        List<SysFile> items = this.getEntityQueryableBySql(sql).toList();

        for (SysFile one : items) {
            // 1.删除数据库
            this.easyEntityQuery.deletable(SysFile.class)
                    .disableLogicDelete()
                    .allowDeleteStatement(true)
                    .where(s -> s.fileId().eq(one.getFileId()))
                    .executeRows();

            // 2.删除本地文件
            if (!StringUtils.equalsIgnoreCase(one.getFileType(), "Folder")) {
                if (one.getFileHash() != null) {
                    long count = this.easyEntityQuery.queryable(SysFile.class)
                            .where(r -> r.fileHash().eq(one.getFileHash()))
                            .count();

                    // 该文件只有一个引用, 则删除文件
                    if (count == 1) {
                        Path realFile = this.service.getFilePath(one.getFileHash());

                        Files.deleteIfExists(realFile);
                    }
                }
            }
        }

        return true;
    }

    /**
     * <p>
     * 根据APPID查询系统文件信息
     * </p>
     */
    public List<SysFile> findByCategoryIdCore(String appId) throws IOException {
        var subQuery = this.easyEntityQuery.queryable(SysAppFileCategory.class)
                .where(s -> s.appId().eq(appId))
                .select(s -> s.fileCategoryId());

        var query = this.easyEntityQuery.queryable(SysFile.class)
                .where(r -> r.categoryId().in(subQuery));

        return query.toList();
    }

    public List<SysFile> findByAppModuleIdCore(String appModuleId) throws IOException {
        var subQuery = this.easyEntityQuery.queryable(SysAppModuleFileCategory.class)
                .where(s -> s.appModuleId().eq(appModuleId))
                .select(s -> s.fileCategoryId());

        var query = this.easyEntityQuery.queryable(SysFile.class)
                .where(r -> r.categoryId().in(subQuery));

        return query.toList();
    }
}
